﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.IO.Ports;
using System.Net.Sockets;
using Modbus.Device;
using Modbus.Data;
using System.IO;
using System.Diagnostics;

namespace ModbusTestTool.Singeltons
{
    public enum serialOrTcp { tcp, serial };

    [DataContract()]
    class stateCom : state
    {   [DataMember()]
        public string comport { set { createPort(); port.PortName =getComPortName(value);  } get { return port.PortName; } }
        [DataMember()]
        public int baudrate   { set { createPort();  port.BaudRate = value; } get { return port.BaudRate; } }
        private SerialPort port;

        public stateCom(string comport, int baudrate, byte modbusid, string tag)
            : base()
        {

            createPort();

            this.comport = getComPortName(comport); 
            this.baudrate = baudrate;
            this.modbusId = modbusid;
            this.tag = tag;

            //port = new SerialPort(comport, baudrate, Parity.None, 8, StopBits.One);
        }

        public stateCom()
        {
            createPort();
        }

        /// <summary>
        /// The other function returns all the port data such as
        /// COM82222 Standard Serial over Bluetooth link (COM8)
        /// This regex gets only the comport name
        /// </summary>
        /// <param name="input">The entire string </param>
        /// <returns></returns>
        private string getComPortName(string input){
            return System.Text.RegularExpressions.Regex.Match(input, "COM[0-9]+").Captures[0].Value;
        }

        private void createPort()
        {
            if (port == null)
            {
                port = new SerialPort();
                port.Parity = Parity.None;
                port.StopBits = StopBits.One;
            }
        }

        public override void openPort(){

            lock (lockVariable)
            {
                if (!port.IsOpen)
                {
                    try
                    {
                        port.Open();
                    }
                    catch (IOException err)
                    {
                        if (err.Message == "The I/O operation has been aborted because of either a thread exit or an application request.\r\n")
                        {
                            System.Windows.Forms.MessageBox.Show(" Trying to fix");
                            ModbusTestTool.Modbus.SerialPortFixer.Execute(port.PortName);
                            port.Open();
                        }
                    }
                }
            }
        }

        public override void closePort()
        {
            lock (lockVariable)
            {
                if (port.IsOpen)
                {
                    port.Close();
                }
            }
        }

        public override string Initialize()
        {
            lock (lockVariable)
            {
                createPort();
                master = ModbusSerialMaster.CreateRtu(port);
            }
            return tag;
        }

        public override string getPortDetails()
        {
            return port.PortName;
        }

        public override bool isOpen()
        {
            if (port == null)
                return false;

            return port.IsOpen;
        }
    }

    [DataContract()]
    public class stateIp : state
    {
        [DataMember()]
        public string address;
        
        [DataMember()]
        public int port;
        private TcpClient socket;
        
        public stateIp():base()
        {   
        }

        public stateIp(string address, int port, byte modbusId, string tag):base()
        {

            
            this.address = address;
            this.port = port;
            this.modbusId = modbusId;
            this.tag = tag;
            //Initialize();
            
        }

        public override void openPort()
        {
            lock (lockVariable)
            {


                try
                {
                    if (!socket.Connected)
                        socket.Connect(address, port);
                }
                catch (ObjectDisposedException)
                {
                    socket = new TcpClient();
                    socket.Connect(address, port);
                }
                catch (NullReferenceException)
                {
                    socket = new TcpClient();
                    socket.Connect(address, port);
                }
            }
        }

        public override void closePort()
        {
            lock (lockVariable)
            {
                if (socket.Connected)
                    socket.Close();
            }
        }

        public override string Initialize()
        {



            lock (lockVariable)
            {
                socket = new TcpClient();
                master = ModbusSerialMaster.CreateRtu(socket);
            }

            return tag;
        }

        public override string getPortDetails()
        {
            return string.Format("{0} {1}", this.address, this.port);
        }

        public override bool isOpen()
        {
            if (socket == null)
                return false;
            try
            {
                return socket.Connected;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }

    /// <summary>
    /// This class is a base class that can used with datacontractserialization to do serialize different states.
    /// </summary>
    [DataContract()]
    [KnownType(typeof(stateCom))]
    [KnownType(typeof(stateIp))]
    abstract public  class  state
    {
        [DataMember()]
        public string tag;
        [DataMember()]
        public byte modbusId;
        
        public ModbusMaster master;
        protected object lockVariable;

        // default data contstructor for xml serialization
        public state()
        {
            lockVariable = new object();
        }

        public abstract void openPort();


        public abstract void closePort();

        /// <summary>
        /// After serialization call this function to intialize the private classes
        /// </summary>
        public abstract string Initialize();

        public void InitializeLock()
        {
            lockVariable = new object();
        }

        public abstract string getPortDetails();

        public abstract bool isOpen();
        
       
    }





    public class NewStateEvenArgs : EventArgs
    {
        public String[] values;
    }



    /// <summary>
    /// This class will contain the different ports to
    /// hold the data
    /// </summary>
    class SettingsSingelton
    {
        List<state> _storageStates = new List<state>();
        static SettingsSingelton _instance = new  SettingsSingelton();
        Dictionary<string, state> _storageDict = new Dictionary<string, state>();
        const string settingsFile = "settings.xml";

        public delegate void NewStateHandler(object sender,  NewStateEvenArgs data);
        public event NewStateHandler NewStateChanged;
        


        public static SettingsSingelton Instance
        {
            get
            {
                return _instance;
            }
        }

        //

        public void addState(state item)
        {
            _storageStates.Add(item);
            _storageDict.Add(item.tag, item);

            StateChanged();
        }

        private void StateChanged()
        {
            if (NewStateChanged != null)
            {
                NewStateChanged(this, new NewStateEvenArgs() { values = getListState().ToArray() });
            }
        }

        


        public List<string> getListState()
        {
            var listOfStates = from n in _storageStates
                               select  n.tag ;

            List<string> temp = new List<string>();

            foreach (string item in listOfStates)
            {
                temp.Add(item);
            }


            return temp;
        }

        public state getState(string name)
        {
            if (name == null)
                return null;

            if (!_storageDict.ContainsKey(name))
            {
                return null;
            }


            return _storageDict[name];
        }

        public state getState(IModbusMaster master)
        {
            foreach (state item in _storageStates)
            {
                if (item.master == master)
                    return item;
            }

            return null;
        }

        public void saveStates()
        {
            DataContractSerializer serializer = new DataContractSerializer(typeof(List<state>));
            using (Stream outHandle = File.Create(settingsFile))
            {
                serializer.WriteObject(outHandle, _storageStates);
            }
        }

        ~SettingsSingelton()
        {
            try
            {
                //saveStates();
            }
            catch (Exception)
            {
                //System.Windows.Forms.MessageBox.Show(err.Message);
                //Debug.WriteLine(err.Message);
                //Console.WriteLine(err.Message);
                //using (StreamWriter log = new StreamWriter("log.txt"))
                //{
                //    log.WriteLine(err.Message);
                //}

            }
         
        }


        public void readStates()
        {
            if (File.Exists(settingsFile))
            {
                DataContractSerializer serializer = new DataContractSerializer(typeof(List<state>));
                using (Stream outHandle = File.Open(settingsFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    _storageStates = (List<state>)serializer.ReadObject(outHandle);
                }

                foreach (state item in _storageStates)
                {
                    _storageDict.Add(item.tag, item);
                }

                foreach (state item in _storageStates)
                {
                    item.InitializeLock();
                }
            }
        }

        public void deleteState(string tag)
        {
            _storageStates.Remove(_storageDict[tag]);
            _storageDict.Remove(tag);
            StateChanged();
        }

        /// <summary>
        /// Private class constructor
        /// </summary>
        private SettingsSingelton() {
            readStates();
        }
    }
}
